require "Scene.Item.ItemHandler"

--[[
Item is a basic concept of the game, it could be created, exchanged, used and
deleted, there are several attributes of item:

droppable -- static attribute, whether the item could be deleted by drop operation
sellable  -- static attribute, whether the item could be selled to system
bind 	  -- dynamic attribute, whether the item could be traded between players

only items have the same id could be accmulated, item id is different if item
have the different dynamic attribute

item = {
	id = string, 		   -- item identifier
	templateData = object, -- item template data structure
	bind = boolean, 	   -- if the item is binded
	data = object, 		   -- item instance data(etc. Equip properites)
}

BagService provides storage and manipulation interface for item
--]]
ItemService = {}

local itemId = 1

local TABLE_SIZE = 10000

local TEMPLATE_TABLE = {
	"Item"
}

local TEMPLATE_SQL = {
	"SELECT * FROM Item WHERE id = @id;"
}

local TEMPLATE_CACHE = {}

local function getItemId(templateData, bind)
	local templateId = templateData.id
	local accmulation = tonumber(templateData.accmulation)

	if accmulation < 1 then
		error("Invalid accmulation " .. templateData.accmulation .. " for item " .. templateId)
	end

	-- if the item could not be accmulated, give it an unique id
	if accmulation == 1 then
		local id = templateId .. "_" .. tostring(Util.getMiliSeconds()) .. "_" .. itemId
		itemId = itemId + 1
		return id
	elseif bind == true then
		return templateId .. "_1"
	else
		return templateId .. "_0"
	end
end

ItemService.getTemplateData = function(templateId)
	templateId = tostring(templateId)
	if TEMPLATE_CACHE[templateId] then
		return TEMPLATE_CACHE[templateId]
	end

	local tableIdx = math.floor(tonumber(templateId) / TABLE_SIZE)
	local tempalteSQL = TEMPLATE_SQL[tableIdx]

	if not tempalteSQL then
		error("Invalid template id " .. templateId)
	end

	local status, count, rows = Template.read(tempalteSQL, { id = templateId })
	if not status or count ~= 1 then
		error("Invalid template id " .. templateId)
	end

	TEMPLATE_CACHE[templateId] = rows[1]
	return TEMPLATE_CACHE[templateId]
end

ItemService.newItem = function(templateId, bind)
	assert(type(bind) == "boolean")

	local templateData = ItemService.getTemplateData(templateId)
	local item = {
		id = getItemId(templateData, bind),
		templateData = templateData,
		bind = bind,
		data = {}
	}
	return item
end

ItemService.getItem = function(data)
	local templateData = ItemService.getTemplateData(data.templateId)
	local item = {
		id = data.id,
		templateData = templateData,
		bind = data.bind,
		data = data.data
	}

	if not item.data then
		item.data = {}
	end
	return item
end

ItemService.getData = function(item)
	local data = {
		id = item.id,
		templateId = item.templateData.id,
		data = item.data,
		bind = item.bind
	}
	return data
end

ItemService.getTemplateId = function(item)
	return item.templateData.id
end

ItemService.getAccmulation = function(item)
	return tonumber(item.templateData.accmulation)
end

ItemService.cloneItem = function(item)
	return ItemService.newItem(ItemService.getTemplateId(item), item.bind)
end

ItemService.canAdd = function(item, count)
	assert(count > 0, "Add invalid count " .. tostring(count) .. " for item " .. item.id)

	local accmulation = ItemService.getAccmulation(item)
	if accmulation == 1 then
		if count ~= 1 then
			error("Add invalid count " .. tostring(count) .. " for item " .. item.id)
		end
		return 1
	else
		return math.min(accmulation, count)
	end
end

ItemService.canAccmulate = function(srcItem, srcCount, dstItem, dstCount)
	assert(srcCount > 0, "Accmulate invalid count " .. tostring(srcCount) .. " for item " .. srcItem.id)
	assert(dstCount > 0, "Accmulate invalid count " .. tostring(dstCount) .. " for item " .. dstItem.id)

	if srcItem.id ~= dstItem.id then
		return 0 -- only item with the same id could be accmulated
	end

	local accmulation = ItemService.getAccmulation(srcItem)
	if accmulation == 1 then
		error("Add existed item " .. srcItem.id)
	end

	local available = math.min(accmulation - dstCount, srcCount)
	if available > 0 then
		return available
	else
		return 0 -- if accmulation is adjusted unproperly
	end
end

ItemService.canDrop = function(item)
	return true
end

ItemService.canSell = function(item)
	return true
end

ItemService.canTrade = function(item)
	return (not item.bind)
end

ItemService.bind = function(item)
	if item.bind then
		return false
	end

	local templateId = ItemService.getTemplateId(item)
	local accmulation = ItemService.getAccmulation(item)

	if accmulation > 1 then
		item.id = templateId .. "_1"
	end
	item.bind = true

	return true
end

ItemService.use = function(player, item, count, ...)
	assert(count > 0, "Use invalid count " .. tostring(count) .. " for item " .. item.id)

	local handler = ItemHandler[item.templateData.serverHandler]
	if not handler then
		Log.error("Use item " .. item.id .. " count " .. tostring(count) .. " without handler")
		return false -- could not use a item without function, it's meanless
	end

	-- an item handler should return if the item could be used or not
	-- 1. some operation like change scene, is not allowed in a item handler,
	-- if it is necessary, wrap it with Async module
	-- 2. in some case, the item function will lead to add more items, make
	-- sure there are enough cells for them
	local status, success = tryCall(handler, player, item, count, ...)
	return status and (success == true)
end
